#include <media_lib.h>
#include <mytbf.h>
#include <glob.h>
#include <proto.h>
#include <server_conf.h>
#include <stdio.h>
#include <stdlib.h>
#include <syslog.h>
#include <sys/types.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>

#define PATHSIZE 128
#define LINEBUFSIZE 4096

// 媒体频道结构体(详细)
struct channel_context_st {
    chnid_t chnid;
    char* desc; // 当前频道的描述信息
    glob_t mp3glob; // 当前频道的媒体文件存储容器
    int pos;    // 当前文件对应mp3glob.gl_pathv的编号
    int fd; // 文件描述符
    off_t offset;   // 当前fd内容里的offset
    mytbf_t* tbf;   // 流量控制实例;开环流控：不管对端是否接收到
};

static struct channel_context_st channels[MAXCHNID];  // 媒体频道

static int glob_err(const char* str, int errNo) {
    fprintf(stderr, "%s,%d,%s\n", str, errNo, strerror(errno));
    return errNo;
}

#define MP3_BIT_RATE 128*1024   // 200kbit/s，sendto报错(msg too long)
static struct channel_context_st* path2entry(const char* path) {
    char pathstr[PATHSIZE] = {0};
    char linebuf[LINEBUFSIZE] = {0};
    static chnid_t cur_chnid = MINCHNID;
    struct channel_context_st* me = &channels[cur_chnid-1];
    FILE* fp = NULL;

    // 获取频道概览信息文件
    strncpy(pathstr, path, PATHSIZE);
    strncat(pathstr, "/desc", PATHSIZE);

    // 打开频道概览信息文件
    fp = fopen(pathstr, "r");
    if (fp == NULL) {
        syslog(LOG_ERR, "paht:%s,fopen desc error\n", path);
        return NULL;
    }

    // 读取频道概览信息
    if (fgets(linebuf, LINEBUFSIZE, fp) == NULL) {
        syslog(LOG_INFO, "%s is not a channel dir\n", pathstr);
        fclose(fp);
        return NULL;
    }

    // 创建当前频道的令牌桶
    me->tbf = mytbf_init(MP3_BIT_RATE/8, MP3_BIT_RATE/8*10);
    if (me->tbf == NULL) {
        syslog(LOG_ERR, "mytbf_init error\n");
        fclose(fp);
        return NULL;
    }

    // 保存当前频道的描述信息
    me->desc = strdup(linebuf); // strdup内部调用了malloc

    // 获取当前频道的媒体文件的路径
    strncpy(pathstr, path, PATHSIZE);
    strncat(pathstr, "/*.mp3", PATHSIZE);
    // 保存当前频道的媒体文件到glob_t结构
    if (glob(pathstr, 0, glob_err, &(me->mp3glob)) != 0) {
        syslog(LOG_ERR, "glob error\n");
        fclose(fp);
        return NULL;
    }

    me->pos = 0;    // 媒体文件索引
    me->offset = 0; // 媒体文件流位置
    me->fd = open(me->mp3glob.gl_pathv[me->pos], O_RDONLY);
    if (me->fd < 0) {
        syslog(LOG_ERR, "open error\n");
        fclose(fp);
        return NULL;
    }

    fclose(fp);

    me->chnid = cur_chnid;
    cur_chnid++;
    
    return me;
}

/*
 \brief 当前频道打开下一个媒体文件
 \param chnid:协议里规定的频道号
*/
static int open_next(chnid_t chnid) {
    int chnIndex = chnid-1; // 协议里频道号在channels结构里的索引
    char* file = NULL;
    int openErrCnt = 0; // open系统调用失败的次数
    int fileNum = channels[chnIndex].mp3glob.gl_pathc; // 当前频道包含的媒体文件个数
    close(channels[chnIndex].fd);  // 关闭上一个文件对应的文件描述符
    while (1) {
        if (openErrCnt >= fileNum) {
            break;
        }
        channels[chnIndex].pos++;  // 选取下一个媒体文件
        if (channels[chnIndex].pos == fileNum) {  // 超过了最后一个媒体文件
            channels[chnIndex].pos = 0;    // 重新选择第一个媒体文件
        }
        channels[chnIndex].fd = -1;
        file = channels[chnIndex].mp3glob.gl_pathv[channels[chnIndex].pos];
        channels[chnIndex].fd = open(file, O_RDONLY);
        if (channels[chnIndex].fd < 0) {
            syslog(LOG_WARNING, "open %s error\n", file);
            openErrCnt++;
        }
        else {
            channels[chnIndex].offset = 0;  // 定位到新媒体文件的内容的头部
            return 0;
        }
    }
    return -1;
}

int mlib_getchnlist(struct mlib_listentry_st** result, int* size) {
    int i = 0;
    char path[PATHSIZE] = {0};
    struct mlib_listentry_st* ptr = NULL;
    struct channel_context_st* res = NULL;  // 频道详细信息
    int num = 0;
    int ret = -1;
    glob_t globres;

    snprintf(path, PATHSIZE, "%s/*", conf.mediaDir);
    
    // 解析目录，获得频道目录
    ret = glob(path, 0, glob_err, &globres);
    if (ret != 0) {
        exit(1);
    }

    // 频道概览信息内存空间，根据目录个数生成
    ptr = (struct mlib_listentry_st*)malloc(sizeof(struct mlib_listentry_st)*globres.gl_pathc);
    if (ptr == NULL) {
        syslog(LOG_ERR, "malloc error,%s\n", strerror(errno));
        exit(1);
    }

    for (i = 0; i < globres.gl_pathc; i++) {
        res = path2entry(globres.gl_pathv[i]);
        if (res != NULL) {
            syslog(LOG_INFO, "path2entry(%s) return chnid:%d,decs:%s", globres.gl_pathv[i], res->chnid, res->desc);
            memcpy(channels+res->chnid, res, sizeof(*res));
            ptr[num].chnid = res->chnid;
            ptr[num].desc = strdup(res->desc);  // strdup内部使用了malloc
            num++;
        }
    }

    // 目录个数不等于频道个数时，根据频道个数重新分配内存
    if (num != globres.gl_pathc) {
        ptr = realloc(ptr, sizeof(struct mlib_listentry_st)*num);
    }
    *result = ptr;
    *size = num;

    return 0;
}
int mlib_freechnlist(struct mlib_listentry_st* ptr) {
    if (ptr)
        free(ptr);
        ptr = NULL;
    return 0;
}

ssize_t mlib_readchn(chnid_t chnid, void* buf, size_t size) {
    int tbfsize = 0;    // 实际取得的令牌个数
    ssize_t len = 0;    // pread一次读取到的字节数
    int ret = -1;
    int chnIndex = chnid-1;

    tbfsize = mytbf_fetch_token(channels[chnIndex].tbf, size);

    while (1) {
        len = pread(channels[chnIndex].fd, buf, tbfsize, channels[chnIndex].offset);
        if (len < 0) {
            syslog(LOG_ERR, "pread error\n");
            ret = open_next(chnid);
            if (ret == -1) {
                syslog(LOG_ERR, "chnid:%d have no valid media file\n", chnid);
                exit(1);
            }
        }
        else if (len == 0) {    // 当前频道的当前文件读取完
            // syslog(LOG_INFO, "pread eof\n");
            ret = open_next(chnid);
            if (ret == -1) {
                syslog(LOG_ERR, "chnid:%d have no valid media file\n", chnid);
                exit(1);
            }
        }
        else if (len > 0) {
            channels[chnIndex].offset += len;  // 更新当前媒体文件内容的读取位置
            break;
        }
    }

    if (tbfsize > 0) {
        mytbf_fetch_token(channels[chnIndex].tbf, tbfsize);
    }

    return len;
}
